#!/bin/bash

# 脚本：CentOS 7.8 系统更新和 OpenSSH 高级更新脚本 (南京大学镜像源 & 源码编译)
# 功能：
#   1. 修改YUM源为南京大学 (直接写入repo文件)
#   2. 更新所有系统补丁
#   3. 自动更新OpenSSL到当前最新版本 (源码编译安装，使用腾讯云镜像源)
#   4. 自动更新OpenSSH到当前最新版本 (源码编译安装，使用腾讯云镜像源)
#   5. 修改SSH端口为随机端口 (20000-40000)，确保认证设置并配置防火墙/SELinux
#   6. 显示最终系统、OpenSSL、OpenSSH版本及SSH端口

# --- 函数：带延迟的自动确认 ---
auto_confirm_with_delay() {
    local prompt_message="$1"
    local delay="$2"
    echo ""
    echo "$prompt_message"
    echo "将在 $delay 秒后自动继续..."
    for ((i=$delay; i>=1; i--)); do
        echo -n "$i "
        sleep 1
    done
    echo "继续执行。"
    return 0
}

# --- 安全警告和用户确认 (带倒计时) ---
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "!! 警告：CentOS 7 已于 2024 年 6 月 30 日停止维护 (EOL)。                  !!"
echo "!! 您当前的操作系统 CentOS 7.8 已不受官方支持，可能存在严重安全风险。      !!"
echo "!! 强烈建议您尽快迁移到 CentOS Stream、AlmaLinux、Rocky Linux 等受支持的 OS。!!"
echo "!! 此脚本连接的 YUM 镜像是 CentOS 7 的归档镜像。                           !!"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
auto_confirm_with_delay "您理解以上风险并希望继续执行此脚本吗？" 6

# --- 检查是否以 root 权限运行 ---
if [[ $EUID -ne 0 ]]; then
   echo "错误：此脚本必须以 root 权限运行。"
   exit 1
fi

echo "脚本开始执行..."
echo "当前时间: $(date)"

# --- 1. 修改 YUM 镜像源为南京大学 ---
BACKUP_DIR="/etc/yum.repos.d/backup_$(date +%Y%m%d%H%M%S)"
echo "正在备份 /etc/yum.repos.d/ 中的 CentOS-*repo 和 epel*.repo 文件到 $BACKUP_DIR ..."
mkdir -p "$BACKUP_DIR"
repo_files_found=0
if ls /etc/yum.repos.d/CentOS-*.repo 1> /dev/null 2>&1; then
    mv /etc/yum.repos.d/CentOS-*.repo "$BACKUP_DIR/"
    echo "CentOS-*repo 文件备份完成。"
    repo_files_found=1
fi
if ls /etc/yum.repos.d/epel*.repo 1> /dev/null 2>&1; then
    mv /etc/yum.repos.d/epel*.repo "$BACKUP_DIR/"
    echo "epel*.repo 文件备份完成。"
    repo_files_found=1
fi

if [ "$repo_files_found" -eq 0 ]; then
    echo "没有找到标准的 CentOS-*repo 或 epel*.repo 文件进行备份，可能已被修改或已是第三方源。"
fi

echo "正在创建 /etc/yum.repos.d/CentOS-Base.repo 文件 (南京大学镜像源)..."
cat << EOF > /etc/yum.repos.d/CentOS-Base.repo
[base]
name=CentOS-\$releasever - Base - mirrors.nju.edu.cn
baseurl=https://mirrors.nju.edu.cn/centos/\$releasever/os/\$basearch/

[updates]
name=CentOS-\$releasever - Updates - mirrors.nju.edu.cn
baseurl=https://mirrors.nju.edu.cn/centos/\$releasever/updates/\$basearch/

[extras]
name=CentOS-\$releasever - Extras - mirrors.nju.edu.cn
baseurl=https://mirrors.nju.edu.cn/centos/\$releasever/extras/\$basearch/

[centosplus]
name=CentOS-\$releasever - Plus - mirrors.nju.edu.cn
baseurl=https://mirrors.nju.edu.cn/centos/\$releasever/centosplus/\$basearch/
EOF
echo "/etc/yum.repos.d/CentOS-Base.repo 文件创建完成。"

auto_confirm_with_delay "是否需要配置南京大学的 EPEL 源? (EPEL 包含许多额外的常用软件包)" 6
echo "正在创建 /etc/yum.repos.d/epel.repo 文件 (南京大学镜像源)..."
cat << EOF > /etc/yum.repos.d/epel.repo
[epel]
name=Extra Packages for Enterprise Linux \$releasever - \$basearch - mirrors.nju.edu.cn
baseurl=https://mirrors.nju.edu.cn/epel/\$releasever/\$basearch/
failovermethod=priority
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
EOF
echo "/etc/yum.repos.d/epel.repo 文件创建完成。"
epel_configured=1

echo "清理 YUM 缓存..."
yum clean all
echo "生成新的 YUM 缓存..."
yum makecache
if [ $? -ne 0 ]; then
    echo "错误：YUM makecache 失败。请检查源配置或网络。"
    exit 1
fi
echo "YUM 镜像源已成功修改为南京大学，并已更新缓存。"

# --- 2. 更新系统补丁 ---
echo ""
echo "### 步骤 2: 正在全面更新系统补丁 (来自南京大学归档镜像) ###"
yum update -y
if [ $? -ne 0 ]; then
    echo "警告：系统更新过程中可能发生了一些错误。请检查上面的 YUM 输出。"
else
    echo "系统补丁已成功更新至南京大学归档仓库中的最新版本。"
fi

# --- 3. 获取最新 OpenSSL 和 OpenSSH 版本 ---
OPENSSL_VERSION="${OPENSSL_VERSION_AUTO:-1.1.1w}"
if [ "$OPENSSL_VERSION" == "1.1.1" ]; then
    OPENSSL_VERSION="1.1.1w"
    echo "OpenSSL 版本修正为: $OPENSSL_VERSION (自动检测可能只返回主版本号)"
fi

echo "正在自动检测最新的 OpenSSH 版本..."
OPENSSH_MIRROR_URL="https://mirrors.cloud.tencent.com/OpenBSD/OpenSSH/portable/"
LATEST_OPENSSH_TARBALL=$(curl -s "$OPENSSH_MIRROR_URL" | grep -oE 'openssh-[0-9]+\.[0-9]+p[0-9]+\.tar\.gz' | sort -V | tail -n1)

if [ -z "$LATEST_OPENSSH_TARBALL" ]; then
    echo "警告：未能自动检测到最新 OpenSSH 版本，使用默认版本 10.0p1"
    SSH_VERSION="10.0p1"
else
    SSH_VERSION=$(echo "$LATEST_OPENSSH_TARBALL" | sed -E 's/openssh-(.*)\.tar\.gz/\1/')
    echo "检测到最新 OpenSSH 版本为: $SSH_VERSION"
fi

echo "自动检测到的 OpenSSL 版本: $OPENSSL_VERSION"
echo "自动检测到的 OpenSSH 版本: $SSH_VERSION"

# --- 4. 更新 OpenSSL (源码编译安装) ---
echo ""
echo "### 步骤 4: 正在更新 OpenSSL (源码编译安装) ###"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "!! 警告：从源码编译安装 OpenSSL 具有较高风险，可能影响系统稳定性。        !!"
echo "!! 请务必在执行前备份关键文件并谨慎操作！                               !!"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo ""

OPENSSL_TARBALL="openssl-${OPENSSL_VERSION}.tar.gz"
OPENSSL_DOWNLOAD_URL="https://mirrors.cloud.tencent.com/openssl/source/${OPENSSL_TARBALL}"
OPENSSL_BUILD_DIR="/usr/local/src/openssl_build"
OPENSSL_INSTALL_DIR="/usr/local/openssl"

auto_confirm_with_delay "是否需要编译安装 OpenSSL ${OPENSSL_VERSION}？" 6

echo "正在安装 OpenSSL 编译所需的依赖软件包 (perl, zlib-devel)..."
yum install -y perl zlib-devel
if [ $? -ne 0 ]; then
    echo "错误：安装 perl 或 zlib-devel 失败。请检查 YUM 输出。"
    exit 1
fi
echo "依赖软件包安装完成。"

mkdir -p "$OPENSSL_BUILD_DIR"
cd "$OPENSSL_BUILD_DIR" || { echo "错误：无法进入 $OPENSSL_BUILD_DIR 目录。"; exit 1; }

echo "正在下载 OpenSSL 源码: ${OPENSSL_DOWNLOAD_URL} (使用腾讯云镜像源)..."
if command -v wget &>/dev/null; then
    wget "$OPENSSL_DOWNLOAD_URL"
elif command -v curl &>/dev/null; then
    curl -L -O "$OPENSSL_DOWNLOAD_URL"
fi

if [ $? -ne 0 ]; then
    echo "错误：OpenSSL 源码下载失败。请检查版本号或网络连接。"
    rm -rf "$OPENSSL_BUILD_DIR"
    exit 1
fi
echo "OpenSSL 源码下载完成。"

echo "正在解压源码包..."
tar -xzf "$OPENSSL_TARBALL"
if [ $? -ne 0 ]; then
    echo "错误：解压 OpenSSL 源码包失败。"
    rm -rf "$OPENSSL_BUILD_DIR"
    exit 1
fi
echo "源码包解压完成。"

cd "openssl-${OPENSSL_VERSION}" || { echo "错误：无法进入 openssl-${OPENSSL_VERSION} 目录。"; exit 1; }

echo "正在配置、编译和安装 OpenSSL..."
./config --prefix="$OPENSSL_INSTALL_DIR" shared zlib
if [ $? -ne 0 ]; then
    echo "错误：OpenSSL 配置失败。请检查输出信息。"
    exit 1
fi

make
if [ $? -ne 0 ]; then
    echo "错误：OpenSSL 编译失败。请检查输出信息。"
    exit 1
fi

make install
if [ $? -ne 0 ]; then
    echo "错误：OpenSSL 安装失败。请检查输出信息。"
    exit 1
fi
echo "OpenSSL 编译安装完成到 $OPENSSL_INSTALL_DIR。"

echo "$OPENSSL_INSTALL_DIR/lib" > /etc/ld.so.conf.d/openssl-local.conf
ldconfig
echo "动态链接库配置完成。"

echo "正在清理 OpenSSL 编译文件..."
rm -rf "$OPENSSL_BUILD_DIR"
echo "清理完成。"
echo "OpenSSL ${OPENSSL_VERSION} 已成功安装并配置。"

# --- 5. 更新 OpenSSH (源码编译安装) ---
echo ""
echo "### 步骤 5: 正在更新 OpenSSH (源码编译安装) ###"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "!! 警告：从源码编译安装 OpenSSH 具有较高风险，可能导致 SSH 服务无法启动，  !!"
echo "!! 从而失去远程访问权限。请务必在执行前备份关键文件并谨慎操作！          !!"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo ""

current_ssh_version_full=$(ssh -V 2>&1)
echo "当前 OpenSSH 版本: $current_ssh_version_full"

auto_confirm_with_delay "是否需要编译安装 OpenSSH ${SSH_VERSION}？" 6

if ! command -v wget &>/dev/null && ! command -v curl &>/dev/null; then
    echo "错误：未找到 wget 或 curl 命令，无法下载 OpenSSH 源码。请手动安装其中一个。"
    exit 1
fi

echo "正在安装 OpenSSH 编译所需的依赖软件包..."
yum install -y gcc make zlib-devel pam-devel libXt-devel
if [ $? -ne 0 ]; then
    echo "错误：安装 OpenSSH 编译依赖失败。请检查 YUM 输出。"
    exit 1
fi
echo "依赖软件包安装完成。"

BUILD_DIR="/usr/local/src/openssh_build"
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR" || { echo "错误：无法进入 $BUILD_DIR 目录。"; exit 1; }

OPENSSH_TARBALL="openssh-${SSH_VERSION}.tar.gz"
OPENSSH_DOWNLOAD_URL="https://mirrors.cloud.tencent.com/OpenBSD/OpenSSH/portable/${OPENSSH_TARBALL}"

echo "正在下载 OpenSSH 源码: ${OPENSSH_DOWNLOAD_URL} (使用腾讯云镜像源)..."
if command -v wget &>/dev/null; then
    wget "$OPENSSH_DOWNLOAD_URL"
elif command -v curl &>/dev/null; then
    curl -L -O "$OPENSSH_DOWNLOAD_URL"
fi

if [ $? -ne 0 ]; then
    echo "错误：OpenSSH 源码下载失败。请检查版本号或网络连接。"
    rm -rf "$BUILD_DIR"
    exit 1
fi
echo "OpenSSH 源码下载完成。"

echo "正在解压源码包..."
tar -xzf "$OPENSSH_TARBALL"
if [ $? -ne 0 ]; then
    echo "错误：解压 OpenSSH 源码包失败。"
    rm -rf "$BUILD_DIR"
    exit 1
fi
echo "源码包解压完成。"

SRC_DIR=$(tar -tf "$OPENSSH_TARBALL" | head -1 | cut -f1 -d"/")
if [ -z "$SRC_DIR" ] || [ ! -d "$SRC_DIR" ]; then
    echo "错误：无法找到解压后的源码目录。"
    exit 1
fi

cd "$SRC_DIR" || { echo "错误：无法进入 $SRC_DIR 目录。"; exit 1; }

SSH_BACKUP_DIR="/etc/ssh_backup_$(date +%Y%m%d%H%M%S)"
echo "正在备份现有 OpenSSH 配置文件和主机密钥到 $SSH_BACKUP_DIR ..."
mkdir -p "$SSH_BACKUP_DIR"
cp -a /etc/ssh/sshd_config "$SSH_BACKUP_DIR/" 2>/dev/null
cp -a /etc/ssh/ssh_config "$SSH_BACKUP_DIR/" 2>/dev/null
cp -a /etc/ssh/ssh_host_* "$SSH_BACKUP_DIR/" 2>/dev/null
echo "OpenSSH 配置和密钥备份完成。"

echo "正在停止并禁用旧的 OpenSSH 服务..."
systemctl stop sshd
systemctl disable sshd
echo "旧服务已停止和禁用。"

echo "正在配置、编译和安装 OpenSSH..."
export CPPFLAGS="-I$OPENSSL_INSTALL_DIR/include"
export LDFLAGS="-L$OPENSSL_INSTALL_DIR/lib"

./configure \
    --prefix=/usr \
    --sysconfdir=/etc/ssh \
    --with-pam \
    --with-ssl-dir="$OPENSSL_INSTALL_DIR" \
    --with-privsep-path=/var/lib/sshd \
    --with-xauth=/usr/bin/xauth \
    --with-md5-passwords \
    --with-libs=-ldl
if [ $? -ne 0 ]; then
    echo "错误：OpenSSH 配置失败。请检查输出信息。"
    exit 1
fi

make
if [ $? -ne 0 ]; then
    echo "错误：OpenSSH 编译失败。请检查输出信息。"
    exit 1
fi

make install
if [ $? -ne 0 ]; then
    echo "错误：OpenSSH 安装失败。请检查输出信息。"
    exit 1
fi
echo "OpenSSH 编译安装完成。"

if [ -f "$BUILD_DIR/$SRC_DIR/sshd_config" ]; then
    mv /etc/ssh/sshd_config /etc/ssh/sshd_config.old_$(date +%Y%m%d%H%M%S)_pre_new_install
    cp "$BUILD_DIR/$SRC_DIR/sshd_config" /etc/ssh/sshd_config
    chmod 600 /etc/ssh/sshd_config
    echo "新版 sshd_config 示例文件已安装到 /etc/ssh/sshd_config。"
else
    echo "警告：未找到新编译的 sshd_config 示例文件。请手动检查 OpenSSH 安装是否完整。"
fi

if [ -f "$BUILD_DIR/$SRC_DIR/ssh_config" ]; then
    mv /etc/ssh/ssh_config /etc/ssh/ssh_config.old_$(date +%Y%m%d%H%M%S)_pre_new_install
    cp "$BUILD_DIR/$SRC_DIR/ssh_config" /etc/ssh/ssh_config
    chmod 644 /etc/ssh/ssh_config
    echo "新版 ssh_config 示例文件已安装到 /etc/ssh/ssh_config。"
else
    echo "警告：未找到新编译的 ssh_config 示例文件。请手动检查 OpenSSH 安装是否完整。"
fi

if [ ! -f /etc/ssh/ssh_host_rsa_key ]; then
    echo "正在生成 SSH 主机密钥..."
    ssh-keygen -A
else
    echo "SSH 主机密钥已存在，跳过生成。"
fi

if [ -f "$BUILD_DIR/$SRC_DIR/contrib/sshd.pam" ]; then
    cp "$BUILD_DIR/$SRC_DIR/contrib/sshd.pam" /etc/pam.d/sshd
    echo "已复制新的 PAM 配置到 /etc/pam.d/sshd。"
else
    echo "警告：未找到新的 PAM 配置文件 'sshd.pam'。请确保 /etc/pam.d/sshd 配置正确。"
fi

if [ ! -f /usr/lib/systemd/system/sshd.service ]; then
    echo "警告：未找到 systemd sshd.service 文件。尝试从源码目录复制。"
    if [ -f "$BUILD_DIR/$SRC_DIR/contrib/sshd.service" ]; then
        cp "$BUILD_DIR/$SRC_DIR/contrib/sshd.service" /usr/lib/systemd/system/sshd.service
        systemctl daemon-reload
        echo "已复制并重新加载 sshd.service 文件。"
    else
        echo "错误：无法找到 sshd.service 文件。请手动创建或修复 systemd 服务。"
        exit 1
    fi
fi

echo "正在测试新的 sshd 配置..."
/usr/sbin/sshd -t
if [ $? -ne 0 ]; then
    echo "错误：新的 sshd 配置测试失败！请检查 /etc/ssh/sshd_config 文件中的错误。"
    exit 1
else
    echo "sshd 配置测试成功。"
fi

echo "OpenSSH 编译安装完成，暂不启动服务，将在端口修改后统一启动。"

# --- 6. 修改 SSH 端口及配置防火墙和 SELinux ---
echo ""
echo "### 步骤 6: 正在修改 SSH 端口并配置防火墙/SELinux ###"

NEW_SSH_PORT=$(($RANDOM % 20001 + 20000))
echo "将 SSH 端口修改为随机端口: $NEW_SSH_PORT"

cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak_port_auth_$(date +%Y%m%d%H%M%S)

sed -i '/^Port /d' /etc/ssh/sshd_config
echo "Port $NEW_SSH_PORT" >> /etc/ssh/sshd_config

if grep -qE "^\s*PermitRootLogin\s" /etc/ssh/sshd_config; then
    sed -i 's/^\s*PermitRootLogin\s.*/PermitRootLogin yes/' /etc/ssh/sshd_config
else
    echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
fi

if grep -qE "^\s*PasswordAuthentication\s" /etc/ssh/sshd_config; then
    sed -i 's/^\s*PasswordAuthentication\s.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
else
    echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config
fi

echo "sshd_config 文件修改完成。"

if command -v firewall-cmd &>/dev/null; then
    firewall-cmd --zone=public --permanent --add-port=${NEW_SSH_PORT}/tcp
    firewall-cmd --reload
    if [ $? -eq 0 ]; then
        echo "Firewalld 已更新并重新加载。"
    else
        echo "警告：Firewalld 配置失败。请手动检查防火墙设置。"
    fi
elif command -v iptables &>/dev/null; then
    iptables -I INPUT -p tcp --dport ${NEW_SSH_PORT} -j ACCEPT
    service iptables save
    echo "Iptables 已更新。"
else
    echo "警告：未找到 firewalld 或 iptables 命令，请手动配置防火墙以允许新端口 $NEW_SSH_PORT。"
fi

if command -v semanage &>/dev/null; then
    if semanage port -l | grep -q "^ssh_port_t.*tcp.*${NEW_SSH_PORT}\\b"; then
        semanage port -d -t ssh_port_t -p tcp ${NEW_SSH_PORT}
    fi
    semanage port -a -t ssh_port_t -p tcp ${NEW_SSH_PORT}
    if [ $? -eq 0 ]; then
        echo "SELinux 端口上下文已添加。"
    else
        echo "警告：SELinux 端口上下文添加失败。"
    fi
else
    echo "警告：未找到 semanage 命令。"
fi

echo "正在测试新的 sshd 配置..."
/usr/sbin/sshd -t
if [ $? -ne 0 ]; then
    echo "错误：新的 sshd 配置测试失败！请检查 /etc/ssh/sshd_config 文件中的错误。"
    exit 1
else
    echo "sshd 配置测试成功。"
fi

echo "正在启动并启用新的 OpenSSH 服务..."
systemctl enable sshd
systemctl start sshd
if [ $? -ne 0 ]; then
    echo "错误：启动新的 OpenSSH 服务失败。请检查日志 (journalctl -xe) 获取详细信息。"
    exit 1
fi
echo "新的 OpenSSH 服务已启动并启用。"

# --- 7. 最终状态报告 ---
echo ""
echo "------------------------------------------------------------------------"
echo "脚本执行完毕。"
echo "重要提示：由于 CentOS 7 已 EOL，通过南京大学下载的也是归档软件包。"
echo "请务必尽快规划迁移到受支持的操作系统！"
echo "------------------------------------------------------------------------"
echo ""

echo "--- 最终系统和软件版本报告 ---"
echo "系统补丁更新状态: 已完成 (来自南京大学归档镜像)"
echo "OpenSSL 版本: $(/usr/local/openssl/bin/openssl version 2>/dev/null || openssl version 2>/dev/null || echo "未知")"
echo "OpenSSH 版本: $(/usr/bin/ssh -V 2>&1 | head -n 1 || echo "未知")"

CURRENT_LISTEN_PORT=$(grep -E '^\s*Port\s' /etc/ssh/sshd_config | awk '{print $2}' | head -n 1)
if [ -z "$CURRENT_LISTEN_PORT" ]; then
    CURRENT_LISTEN_PORT=$(ss -tuln | grep sshd | grep -oP '(?<=:)\d+' | head -n 1)
    if [ -z "$CURRENT_LISTEN_PORT" ]; then
        CURRENT_LISTEN_PORT="未知 (请使用 'ss -tuln | grep sshd' 确认)"
    fi
fi
echo "当前 SSH 监听端口: $CURRENT_LISTEN_PORT"
echo "------------------------------"

if ! rpm -q yum-utils &>/dev/null; then
    echo "正在安装 yum-utils 以检查是否需要重启 (如果尚未安装)..."
    yum install -y yum-utils &>/dev/null
fi

if command -v needs-restarting &>/dev/null && needs-restarting -r; then
    echo "建议重启系统以应用所有更新（特别是内核或 glibc 等核心库的更新）。"
    auto_confirm_with_delay "是否现在重启系统？" 6
    echo "正在重启系统..."
    reboot
else
    echo "根据 'needs-restarting' 工具的判断，当前可能不需要重启。"
    echo "但如果有内核或关键系统库更新，手动重启仍然是一个好习惯。"
fi

exit 0
